﻿using Basic.WxApiRemoteModel;
using Basic.WxApiService.ApiInterface;
using Basic.WxApiService.Interface;
using Basic.WxApiService.WxModel;
using Basic.WxApiService.WxModel.Platform;
using Basic.WxExtendService.Interface;
using Basic.WxModel.PlatformBind;
using WeDonekRpc.CacheClient.Interface;
using WeDonekRpc.Client;
using WeDonekRpc.Helper;

namespace Basic.WxApiService.WxMer
{
    internal class WxMerTokenService : IWxMerTokenService
    {
        private readonly string _CacheKey = "WxToken";
        private readonly IRedisCache _Cache;
        private readonly IWxMerBindCacheService _MerBind;
        private readonly IWxPlatformApi _PlatformApi;
        private readonly IWxPlaformTokenService _PlatformToken;
        public WxMerTokenService (IRedisCache cache,
            IWxPlatformApi platformApi,
            IWxMerBindCacheService merBind,
            IWxPlaformTokenService platformToken)
        {
            this._Cache = cache;
            this._MerBind = merBind;
            this._PlatformApi = platformApi;
            this._PlatformToken = platformToken;
        }

        public WxMerToken GetToken (string wxAppId, WxPrower prower)
        {
            string bindKey = this._MerBind.GetAvailableAppId(wxAppId, prower);
            return this._GetToken(wxAppId, bindKey);
        }
        private WxMerToken _GetToken (string wxAppId, string appId)
        {
            string key = string.Join("_", this._CacheKey, appId, wxAppId);
            if (!this._Cache.TryGet(key, out string token))
            {
                token = this._ResetToken(wxAppId, appId);
            }
            return new WxMerToken(this, token, appId, wxAppId);
        }
        private string _ResetToken (string wxAppId, string appId)
        {
            string key = string.Concat("AccressToken_", appId, "_", wxAppId);
            using (RemoteLock _Lock = RemoteLock.ApplyLock(key, 10, 3))
            {
                if (_Lock.GetLock())
                {
                    WxPlatformBindDto bind = this._MerBind.GetBind(appId, wxAppId);
                    IComponentToken comToken = this._PlatformToken.GetToken(appId);
                    WxMerAuthorizerToken token = this._PlatformApi.GetAccessToken(comToken, bind);
                    key = string.Join("_", this._CacheKey, appId, wxAppId);
                    if (!this._Cache.Set(key, token.authorizer_access_token, new TimeSpan(0, 0, token.expires_in - 5)))
                    {
                        throw new ErrorException("cache.set.fail");
                    }
                    this._MerBind.SetRefreshToken(bind, token.authorizer_refresh_token);
                    _Lock.Exit(token.authorizer_access_token);
                    return token.authorizer_access_token;
                }
                else if (_Lock.IsError)
                {
                    throw new ErrorException(_Lock.Error);
                }
                else
                {
                    return _Lock.Extend;
                }
            }
        }
        public void SetToken (WxAuthorization auth, string appId)
        {
            string key = string.Join("_", this._CacheKey, appId, auth.authorizer_appid);
            _ = this._Cache.Set(key, auth.authorizer_access_token, new TimeSpan(0, 0, auth.expires_in - 5));
        }

        public string ResetToken (string appId, string wxAppId, string token)
        {
            string key = string.Join("_", this._CacheKey, appId, wxAppId);
            if (this._Cache.TryGet(key, out string sour) && sour != token)
            {
                return sour;
            }
            return this._ResetToken(appId, wxAppId);
        }
    }
}
